#ifndef ESTRATEGIAALMACENAMIENTOBLOQUES_H_
#define ESTRATEGIAALMACENAMIENTOBLOQUES_H_

#include <list>
#include <exception> 
#include "EstrategiaAlmacenamiento.h"

namespace Toolkit
{

struct IndiceEspacio{
	unsigned long int posicion;
	unsigned long int bytesDisponibles;
	
	bool operator < (const IndiceEspacio &otro) const
	{
		return this->bytesDisponibles < otro.bytesDisponibles;
	}
	
	bool operator == (const IndiceEspacio &otro) const
	{
		return this->bytesDisponibles == otro.bytesDisponibles;
	}
	
};

//TODO excepciones cuando el tamaño de bloque excede el permitido, ver si no lo esta haciendo
//ya el bloque

class EstrategiaAlmacenamientoBloques : public EstrategiaAlmacenamiento
{
public:
	EstrategiaAlmacenamientoBloques(const float &porcentajeFragmentacion = 0.4);
	virtual ~EstrategiaAlmacenamientoBloques();
	
	virtual unsigned long int guardarDato (Almacenamiento &almacenamiento, Dato &dato,
                    const Clave &clave, const string &nombreAlmacenamiento);

	virtual unsigned long int modificarDato (Almacenamiento &almacenamiento, Dato &dato, const Clave &clave,
                    const unsigned long int posicion, const string &nombreAlmacenamiento);

    virtual void recuperarDato (Almacenamiento &almacenamiento, const unsigned long int posicion, Dato &dato,
                    const Clave &clave, const string &nombreAlmacenamiento);

    virtual void eliminarDato (Almacenamiento &almacenamiento, const long unsigned int posicion,
                    Dato &dato, const Clave &clave, const string &nombreAlmacenamiento);

private:	
	unsigned long int posicionUltimo;
	
	/**
	 * Esta lista se mantiene para controlar la fragmentacino interna en los bloques del almacenamiento.
	 * Se mantiene ordenada ascendentemente, para poder aplicar una política de worst fit.
	 * */	
	list <IndiceEspacio> espacios;
	
	/**
	 * Porcentaje de fragmentacion interna permitido en los bloques del 
	 * almacenamiento.
	 * */
	float porcentajeFragmentacion; 
    
    /**
     * Toma el bloque y la posicion en la que fue insertado, y verifica si hace falta
     * considerarlo como fragmentado, esto eso, si será candidato para futuras inserciones
     * de registros.
     * */
    void controlarFragmentacion(const Bloque &bloque, const unsigned long int &posicion);
    
    /**
     * Agrega el registro al bloque y almacena este ultimo en la posicion dada.
     * Si esta no corresponde al final del archivo, se controla la fragmentacion
     * de ese bloque.
     * */
    void almacenar(Bloque &bloque, Registro &registro, const Clave &clave, 
    			Almacenamiento &almacenamiento, const long unsigned int &posicion);
    
    /**
     * Retira de la lista de espacios el correspondiente a la posicion de bloque
     * dada, si es que está presente.
     * */
    void removerIndice(const long unsigned int &posicion);
    
    /**
     * Para ser llamado después de retirar un indice de un bloque.
     * Si el bloque quedó vacío se lo retira del almacenamiento; en caso 
     * contrario se lo actualiza y se controla la fragmentación interna. 
     * */
    void actualizarEliminacionRegistro(Bloque &bloque, 
    		Almacenamiento &almacenamiento, const long unsigned int &posicion);
    
};

class DesbordeDeDatoException : public exception
{
	virtual const char* what() const throw()
  	{
    	return "La serialización del dato pasado excede el tamaño de bloque especificado";
  	}
};

}

#endif /*ESTRATEGIAALMACENAMIENTOBLOQUES_H_*/
